import React from 'react'
import cssStyle from './index.module.css'
import PropTypes from 'prop-types'
import { Button, Form } from '@/component'

function imgToBase64({img, width, left, top, success}){
    let canvas = document.createElement('canvas')
    canvas.width = img.naturalWidth
    canvas.height = img.naturalHeight
    let context = canvas.getContext('2d')
    context.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight)

    let scale = img.naturalWidth/img.width
    let data = context.getImageData(left*scale, top*scale, width*scale, width*scale)
    let clipCanvas = document.createElement('canvas')
    clipCanvas.width = width*scale
    clipCanvas.height = width*scale
    let clipContext = clipCanvas.getContext('2d')
    clipContext.putImageData(data, 0, 0)
    clipCanvas.toBlob(res => {
        success(res)
    }, 'image/jpeg')
}

class Crop extends React.PureComponent{
    state = {
        hasImg: this.props.src ? true : false
    }
    mainImgRef = React.createRef()
    cropBoxRef = React.createRef()
    cropBoxViewRef = React.createRef()
    prevviewRef = React.createRef()
    containerInfo = {
        left: 0,
        right: 0,
        top: 0,
        bottom: 0
    }
    isMouse = false
    isCropBoxMouse = false
    isPointMouse = false
    timer = null
    cropBoxTimer = null
    pointTimer = null
    point = ''
    src = null
    imgInfo = {
        width: 0,
        height: 0,
        left: 0,
        top: 0,
        translateX: 0,
        translateY: 0,
        start: {
            x: 0,
            y: 0
        },
        current: {
            x: 0,
            y: 0
        }
    }
    cropBoxInfo = {
        currentWidth: 125,
        currentHeight: 125,
        width: 125,
        height: 125,
        top: 62.5,
        left: 62.5,
        translateX: 62.5,
        translateY: 62.5,
        start: {
            x: 0,
            y: 0
        },
        current: {
            x: 0,
            y: 0
        }
    }
    pointInfo = {
        leftTop: {
            start: {
                x: 0,
                y: 0
            },
            current: {
                x: 0,
                y: 0
            }
        },
        rightTop: {
            start: {
                x: 0,
                y: 0
            },
            current: {
                x: 0,
                y: 0
            }
        },
        rightBottom: {
            start: {
                x: 0,
                y: 0
            },
            current: {
                x: 0,
                y: 0
            }
        },
        leftBottom: {
            start: {
                x: 0,
                y: 0
            },
            current: {
                x: 0,
                y: 0
            }
        }
    }
    mouseDown = e => {
        this.isMouse = true
        this.imgInfo.start = {
            x: e.pageX,
            y: e.pageY
        }
        window.addEventListener('mousemove', this.mouseMove)
        window.addEventListener('mouseup', this.mouseUp)
    }
    mouseMove = e => {
        if(this.isMouse){
            if(this.timer){
                cancelAnimationFrame(this.timer)
                this.timer = null
            }
            this.timer = window.requestAnimationFrame(() => {
                this.timer = null
                this.imgInfo.current = {
                    x: e.pageX,
                    y: e.pageY
                }
                let left = this.imgInfo.translateX
                let top = this.imgInfo.translateY
                let distanceX = 0, distanceY = 0
                if(this.imgInfo.width > this.imgInfo.height){
                    distanceX = this.imgInfo.current.x - this.imgInfo.start.x
                    if(distanceX > 0 && left >= 0){
                        this.imgInfo.start.x = this.imgInfo.current.x
                        this.imgInfo.left = left
                        return
                    }
                    if(distanceX < 0 && Math.abs(left) >= (this.imgInfo.width - 250)){
                        this.imgInfo.start.x = this.imgInfo.current.x
                        this.imgInfo.left = left
                        return
                    }
                }else{
                    distanceY = this.imgInfo.current.y - this.imgInfo.start.y
                    if(distanceY > 0 && top >= 0){
                        this.imgInfo.start.y = this.imgInfo.current.y
                        this.imgInfo.top = top
                        return
                    }
                    if(distanceY < 0 && Math.abs(top) >= (this.imgInfo.height - 250)){
                        this.imgInfo.start.y = this.imgInfo.current.y
                        this.imgInfo.top = top
                        return
                    }
                }
                left = this.imgInfo.translateX = this.imgInfo.left + distanceX
                top = this.imgInfo.translateY = this.imgInfo.top + distanceY
                if(left > 0) {
                    this.imgInfo.translateX = 0
                }else{
                    if(Math.abs(left) > (this.imgInfo.width - 250)){
                        this.imgInfo.translateX = 250 - this.imgInfo.width
                    }
                }
                if(top > 0) {
                    this.imgInfo.translateY = 0
                }else{
                    if(Math.abs(top) > (this.imgInfo.height - 250)){
                        this.imgInfo.translateY = 250 - this.imgInfo.height
                    }
                }
                this.mainImgRef.current.style.transform = `translate3d(${this.imgInfo.translateX + 'px'}, ${this.imgInfo.translateY + 'px'}, 0)`
                left = this.imgInfo.translateX
                top = this.imgInfo.translateY
                this.cropBoxRef.current.children[0].style.transform = `translate3d(${left - this.cropBoxInfo.translateX + 'px'}, ${top - this.cropBoxInfo.translateY + 'px'}, 0)`
                let scale = 125/parseFloat(this.cropBoxRef.current.style.width)
                this.prevviewRef.current.style.transform = `translate3d(${(left - this.cropBoxInfo.translateX)*scale + 'px'}, ${(top - this.cropBoxInfo.translateY)*scale + 'px'}, 0) scale(${scale})`
            })
        }
    }
    mouseUp = () => {
        if(this.timer){
            cancelAnimationFrame(this.timer)
            this.timer = null
        }
        this.isMouse = false
        this.imgInfo.left = this.imgInfo.translateX
        this.imgInfo.top = this.imgInfo.translateY
        window.removeEventListener('mousemove', this.mouseMove)
        window.removeEventListener('mouseup', this.mouseUp)
    }
    load = e => {
        let { naturalWidth, naturalHeight } = e.target
        if(naturalWidth > naturalHeight){
            this.mainImgRef.current.style.width = 'auto'
            this.mainImgRef.current.style.height = '100%'
        }else if (naturalWidth < naturalHeight){
            this.mainImgRef.current.style.width = '100%'
            this.mainImgRef.current.style.height = 'auto'
        }else{
            this.mainImgRef.current.style.width = '100%'
            this.mainImgRef.current.style.height = '100%'
        }
        this.mainImgRef.current.style.transform = `translate3d(0, 0, 0)`
        this.imgInfo.width = this.mainImgRef.current.width
        this.imgInfo.height = this.mainImgRef.current.height
        this.prevviewRef.current.style.width = this.cropBoxRef.current.children[0].style.width = this.imgInfo.width + 'px'
        this.prevviewRef.current.style.height = this.cropBoxRef.current.children[0].style.height = this.imgInfo.height + 'px'
        this.prevviewRef.current.style.transform = `translate3d(${-this.cropBoxInfo.translateX + 'px'}, ${-this.cropBoxInfo.translateY + 'px'}, 0) scale(1)`
        this.cropBoxRef.current.children[0].style.transform = `translate3d(${-this.cropBoxInfo.translateX + 'px'}, ${-this.cropBoxInfo.translateY + 'px'}, 0)`
        this.cropBoxViewRef.current.style.transform = this.cropBoxRef.current.style.transform = `translate3d(${this.cropBoxInfo.translateX + 'px'}, ${this.cropBoxInfo.translateY + 'px'}, 0)`
        this.cropBoxRef.current.style.width = this.cropBoxViewRef.current.style.width = this.cropBoxRef.current.style.height = this.cropBoxViewRef.current.style.height = this.cropBoxInfo.width + 'px'
        if(this.src){
            URL.revokeObjectURL(this.src)
            this.src = null
        }
        this.setState({
            hasImg: true
        })
        this.imgInfo.translateX = this.imgInfo.translateY = 0
    }
    cropBoxMouseDown = e => {
        this.isCropBoxMouse = true
        this.cropBoxInfo.start = {
            x: e.pageX,
            y: e.pageY
        }
        window.addEventListener('mousemove', this.cropBoxMouseMove)
        window.addEventListener('mouseup', this.cropBoxMouseUp)
    }
    cropBoxMouseMove = e => {
        if(this.isCropBoxMouse){
            if(this.cropBoxTimer){
                cancelAnimationFrame(this.cropBoxTimer)
                this.cropBoxTimer = null
            }
            this.cropBoxTimer = window.requestAnimationFrame(() => {
                this.cropBoxTimer = null
                this.cropBoxInfo.current = {
                    x: e.pageX,
                    y: e.pageY
                }
                let distanceX = 0, distanceY = 0
                distanceX = this.cropBoxInfo.current.x - this.cropBoxInfo.start.x
                distanceY = this.cropBoxInfo.current.y - this.cropBoxInfo.start.y
                this.cropBoxInfo.translateX = this.cropBoxInfo.left + distanceX
                this.cropBoxInfo.translateY = this.cropBoxInfo.top + distanceY
                let left = this.cropBoxInfo.translateX
                let top = this.cropBoxInfo.translateY
                let width = parseFloat(this.cropBoxRef.current.style.width)
                if(left < 0) {
                    this.cropBoxInfo.left = this.cropBoxInfo.translateX = 0
                    this.cropBoxInfo.start.x = this.cropBoxInfo.current.x
                }else{
                    if(left > (250 - width)){
                        this.cropBoxInfo.left = this.cropBoxInfo.translateX = 250 - width
                        this.cropBoxInfo.start.x = this.cropBoxInfo.current.x
                    }
                }
                if(top < 0) {
                    this.cropBoxInfo.top = this.cropBoxInfo.translateY = 0
                    this.cropBoxInfo.start.y = this.cropBoxInfo.current.y
                }else{
                    if(top > (250 - width)){
                        this.cropBoxInfo.top = this.cropBoxInfo.translateY = 250 - width
                        this.cropBoxInfo.start.y = this.cropBoxInfo.current.y
                    }
                }
                left = this.cropBoxInfo.translateX
                top = this.cropBoxInfo.translateY
                this.cropBoxViewRef.current.style.transform = this.cropBoxRef.current.style.transform = `translate3d(${left + 'px'}, ${top + 'px'}, 0)`
                this.cropBoxRef.current.children[0].style.transform = `translate3d(${this.imgInfo.translateX - left + 'px'}, ${this.imgInfo.translateY - top + 'px'}, 0)`
                let scale = 125/width
                this.prevviewRef.current.style.transform = `translate3d(${(this.imgInfo.translateX - left)*scale + 'px'}, ${(this.imgInfo.translateY - top)*scale + 'px'}, 0) scale(${scale})`
            })
        }
    }
    cropBoxMouseUp = () => {
        if(this.cropBoxTimer){
            cancelAnimationFrame(this.cropBoxTimer)
            this.cropBoxTimer = null
        }
        this.isCropBoxMouse = false
        this.cropBoxInfo.left = this.cropBoxInfo.translateX
        this.cropBoxInfo.top = this.cropBoxInfo.translateY
        window.removeEventListener('mousemove', this.cropBoxMouseMove)
        window.removeEventListener('mouseup', this.cropBoxMouseUp)
    }
    pointMouseDown = (e, point) => {
        e.stopPropagation()
        this.point = point
        this.isPointMouse = true
        this.pointInfo[this.point].start = {
            x: e.pageX,
            y: e.pageY
        }
        window.addEventListener('mousemove', this.pointMouseMove)
        window.addEventListener('mouseup', this.pointMouseUp)
    }
    pointMouseMove = e => {
        e.stopPropagation()
        if(this.pointTimer){
            window.cancelAnimationFrame(this.pointTimer)
            this.pointTimer = null
        }
        if(this.isPointMouse){
            this.pointTimer = window.requestAnimationFrame(() => {
                this.pointTimer = null
                if(!this.point) {
                    return
                }
                this.pointInfo[this.point].current = {
                    x: e.pageX,
                    y: e.pageY
                }
                let distanceX = 0, distanceY = 0
                distanceX = this.pointInfo[this.point].current.x - this.pointInfo[this.point].start.x
                distanceY = this.pointInfo[this.point].current.y - this.pointInfo[this.point].start.y
                if(this.point === 'leftTop' && (this.cropBoxInfo.translateX < 0 || this.cropBoxInfo.translateY < 0) && distanceX < 0){
                    return
                }
                if(this.point === 'rightTop' && ((this.cropBoxInfo.translateX + this.cropBoxInfo.currentWidth > 250) || this.cropBoxInfo.translateY === 0) && distanceX > 0){
                    return
                }
                if(this.point === 'rightBottom' && ((this.cropBoxInfo.translateX + this.cropBoxInfo.currentWidth > 250) || (this.cropBoxInfo.translateY + this.cropBoxInfo.currentWidth > 250)) && distanceX > 0){
                    return
                }
                if(this.point === 'leftBottom' && (this.cropBoxInfo.translateX < 0 || (this.cropBoxInfo.translateY + this.cropBoxInfo.currentWidth > 250)) && distanceX < 0){
                    return
                }
                let distance = Math.abs(distanceX) > Math.abs(distanceY) ? Math.abs(distanceY) : Math.abs(distanceX)
                if(this.point === 'leftTop'){
                    if(!((distanceY > 0 && distanceX > 0) || (distanceY < 0 && distanceX < 0))){
                        return
                    }
                    if(distanceY > 0 && distanceX > 0){
                        this.cropBoxInfo.translateX = this.cropBoxInfo.left + distance
                        this.cropBoxInfo.translateY = this.cropBoxInfo.top + distance
                        this.cropBoxInfo.currentWidth = this.cropBoxInfo.width - distance
                        if(this.cropBoxInfo.currentWidth < 20){
                            this.cropBoxInfo.currentWidth = 20
                            this.cropBoxInfo.translateX = this.cropBoxInfo.left + this.cropBoxInfo.width - 20
                            this.cropBoxInfo.translateY = this.cropBoxInfo.top + this.cropBoxInfo.width - 20
                        }
                    }
                    if(distanceY < 0 && distanceX < 0){
                        this.cropBoxInfo.translateX = this.cropBoxInfo.left - distance
                        this.cropBoxInfo.translateY = this.cropBoxInfo.top - distance
                        this.cropBoxInfo.currentWidth = this.cropBoxInfo.width + distance
                    }
                    if(this.cropBoxInfo.translateX < 0){
                        let over = this.cropBoxInfo.translateX
                        this.cropBoxInfo.translateX = 0
                        this.cropBoxInfo.translateY -= over
                        this.cropBoxInfo.currentWidth += over
                    }
                    if(this.cropBoxInfo.translateY < 0){
                        let over = this.cropBoxInfo.translateY
                        this.cropBoxInfo.translateY = 0
                        this.cropBoxInfo.translateX -= over
                        this.cropBoxInfo.currentWidth += over
                    }
                }else if(this.point === 'rightTop'){
                    if(!((distanceY > 0 && distanceX < 0) || (distanceY < 0 && distanceX > 0))){
                        return
                    }
                    if(distanceX > 0 && distanceY < 0){
                        this.cropBoxInfo.translateY = this.cropBoxInfo.top - distance
                        this.cropBoxInfo.currentWidth = this.cropBoxInfo.width + distance
                    }
                    if(distanceX < 0 && distanceY > 0){
                        this.cropBoxInfo.translateY = this.cropBoxInfo.top + distance
                        this.cropBoxInfo.currentWidth = this.cropBoxInfo.width - distance
                        if(this.cropBoxInfo.currentWidth < 20){
                            this.cropBoxInfo.currentWidth = 20
                            this.cropBoxInfo.translateY = this.cropBoxInfo.top + this.cropBoxInfo.width - 20
                        }
                    }
                    if(this.cropBoxInfo.translateY < 0){
                        let over = this.cropBoxInfo.translateY
                        this.cropBoxInfo.translateY = 0
                        this.cropBoxInfo.currentWidth += over
                    }
                    if(this.cropBoxInfo.translateX + this.cropBoxInfo.currentWidth > 250){
                        let over = 250 - this.cropBoxInfo.currentWidth - this.cropBoxInfo.translateX
                        this.cropBoxInfo.translateY -= over
                        this.cropBoxInfo.currentWidth += over
                    }
                }else if(this.point === 'rightBottom'){
                    if(!((distanceY > 0 && distanceX > 0) || (distanceY < 0 && distanceX < 0))){
                        return
                    }
                    if(distanceY > 0 && distanceX > 0){
                        this.cropBoxInfo.currentWidth = this.cropBoxInfo.width + distance
                    }
                    if(distanceY < 0 && distanceX < 0){
                        this.cropBoxInfo.currentWidth = this.cropBoxInfo.width - distance
                        if(this.cropBoxInfo.currentWidth < 20){
                            this.cropBoxInfo.currentWidth = 20
                        }
                    }
                    if(this.cropBoxInfo.translateX + this.cropBoxInfo.currentWidth > 250){
                        let over = 250 - this.cropBoxInfo.currentWidth - this.cropBoxInfo.translateX
                        this.cropBoxInfo.currentWidth += over
                    }
                    if(this.cropBoxInfo.translateY + this.cropBoxInfo.currentWidth > 250){
                        let over = 250 - this.cropBoxInfo.currentWidth - this.cropBoxInfo.translateY
                        this.cropBoxInfo.currentWidth += over
                    }
                }else{
                    if(!((distanceY > 0 && distanceX < 0) || (distanceY < 0 && distanceX > 0))){
                        return
                    }
                    if(distanceY > 0 && distanceX < 0){
                        this.cropBoxInfo.translateX = this.cropBoxInfo.left - distance
                        this.cropBoxInfo.currentWidth = this.cropBoxInfo.width + distance
                    }
                    if(distanceY < 0 && distanceX > 0){
                        this.cropBoxInfo.translateX = this.cropBoxInfo.left + distance
                        this.cropBoxInfo.currentWidth = this.cropBoxInfo.width - distance
                        if(this.cropBoxInfo.currentWidth < 20){
                            this.cropBoxInfo.currentWidth = 20
                            this.cropBoxInfo.translateX = this.cropBoxInfo.left + this.cropBoxInfo.width - 20
                        }
                    }
                    if(this.cropBoxInfo.translateX < 0){
                        let over = this.cropBoxInfo.translateX
                        this.cropBoxInfo.translateX = 0
                        this.cropBoxInfo.currentWidth += over
                    }
                    if(this.cropBoxInfo.translateY + this.cropBoxInfo.currentWidth > 250){
                        let over = 250 - this.cropBoxInfo.currentWidth - this.cropBoxInfo.translateY
                        this.cropBoxInfo.translateX -= over
                        this.cropBoxInfo.currentWidth += over
                    }
                }
                this.cropBoxViewRef.current.style.transform = this.cropBoxRef.current.style.transform = `translate3d(${this.cropBoxInfo.translateX + 'px'}, ${this.cropBoxInfo.translateY + 'px'}, 0)`
                this.cropBoxRef.current.style.height = this.cropBoxViewRef.current.style.height = this.cropBoxRef.current.style.width = this.cropBoxViewRef.current.style.width = this.cropBoxInfo.currentWidth + 'px'
                let left = this.imgInfo.translateX - this.cropBoxInfo.translateX
                let top = this.imgInfo.translateY - this.cropBoxInfo.translateY
                this.cropBoxRef.current.children[0].style.transform = `translate3d(${left + 'px'}, ${top + 'px'}, 0)`
                let scale = 125/this.cropBoxInfo.currentWidth
                this.prevviewRef.current.style.transform = `translate3d(${left*scale + 'px'}, ${top*scale + 'px'}, 0) scale(${scale})`
            })
        }
    }
    pointMouseUp = () => {
        if(this.pointTimer){
            window.cancelAnimationFrame(this.pointTimer)
            this.pointTimer = null
        }
        this.point = ''
        this.isPointMouse = false
        this.cropBoxInfo.left = this.cropBoxInfo.translateX
        this.cropBoxInfo.top = this.cropBoxInfo.translateY
        this.cropBoxInfo.width = this.cropBoxInfo.currentWidth
        this.cropBoxInfo.height = this.cropBoxInfo.currentHeight
        window.removeEventListener('mousemove', this.pointMouseMove)
        window.removeEventListener('mouseup', this.pointMouseUp)
    }
    getFile = file => {
        this.src = URL.createObjectURL(file)
        this.mainImgRef.current.src = this.cropBoxRef.current.children[0].src = this.prevviewRef.current.src = this.src
    }
    confirm = () => {
        imgToBase64({
            img: this.mainImgRef.current,
            left: Math.abs(this.imgInfo.translateX) + this.cropBoxInfo.translateX,
            top: Math.abs(this.imgInfo.translateY) + this.cropBoxInfo.translateY,
            width: parseFloat(this.cropBoxRef.current.style.width),
            success: res => {
                this.props.success(res)
            }
        })
    }
    componentDidMount(){
    }
    render(){
        return(
            <div className={ cssStyle.crop }>
                <div className={ cssStyle.left }>
                    <div className={ cssStyle.container } draggable="false">
                        <img
                            ref={ this.mainImgRef }
                            src={ this.props.src ? this.props.src : '' }
                            draggable={false}
                            onLoad={ this.load }
                            onDragStart={ () => { return false } }
                        />
                        <div style={{ visibility: this.state.hasImg ? 'visible' : 'hidden' }} onMouseDown={ this.mouseDown }></div>
                    </div>
                    <div style={{ visibility: this.state.hasImg ? 'visible' : 'hidden' }} className={ cssStyle.cropbox } ref={ this.cropBoxRef }>
                        <img
                            src={ this.props.src ? this.props.src : '' }
                            draggable={false}
                            onDragStart={ () => { return false } }
                        />
                    </div>
                    <div style={{ visibility: this.state.hasImg ? 'visible' : 'hidden' }} className={ cssStyle.cropboxView } ref={ this.cropBoxViewRef } onMouseDown={ this.cropBoxMouseDown }>
                        <span
                            onMouseDown={ e => { this.pointMouseDown(e, 'leftTop') } }
                        ></span>
                        <span
                            onMouseDown={ e => { this.pointMouseDown(e, 'rightTop') } }
                        ></span>
                        <span
                            onMouseDown={ e => { this.pointMouseDown(e, 'rightBottom') } }
                        ></span>
                        <span
                            onMouseDown={ e => { this.pointMouseDown(e, 'leftBottom') } }
                        ></span>
                    </div>
                </div>
                <div className={ cssStyle.right }>
                    <div className={ cssStyle.prevview }>
                        <img
                            ref={ this.prevviewRef }
                            src={ this.props.src ? this.props.src : '' }
                        />
                    </div>
                    {
                        this.state.hasImg
                        ?
                        <>
                            <Button type="success" size="small" style={{ marginTop: '10px' }} onClick={ this.confirm }>确定</Button>
                            <Form.upload onChange={ this.getFile } showFileName={ false }>
                                <Button type="primary" size="small" style={{ marginTop: '10px' }}>更换图片</Button>
                            </Form.upload>
                        </>
                        :
                        <Form.upload onChange={ this.getFile } showFileName={ false }>
                            <Button type="primary" size="small" style={{ marginTop: '10px' }}>上传图片</Button>
                        </Form.upload>
                    }
                </div>
            </div>
        )
    }
}

Crop.propTypes = {
    src: PropTypes.string,
    success: PropTypes.func
}

export default Crop